(function($){
	
	// z-index слоев, меняется в процессе показа
	var zIndex = 1;

	// количество открытых лайтбоксов
	var opened = 0;

	// массив модальников
	var modals = [null];

	var defaultOptions	= {
		padding:				[40, 40, 40, 40],			// отступ вокруг лайтбокса
		minWidth:				200,									// минимальная ширина лайтбокса
		minHeight:			100,									// минимальная высота лайтбокса
		maxWidth:				0,										// максимальная ширина лайтбокса, оставить 0, если не ограничивать
		maxHeight:			0,										// максимальная высота лайтбокса, оставить 0, если не ограничивать
		title:					"",										// заголовок окна
		overrides: {
			resize:					null								// оверрайд для функции расчета размеров
		}
	};

	$.osmioModal = function(opts){

		defaultOptions = $.extend(defaultOptions, opts);

	};


	// --------------------------------------------------
	// Сервисные слои

	// Глобальный враппер для всех лайтбоксов
	var modalWrapper = $("<div id='osmio-modal-wrapper'></div>");

	// Слой для дебага
	var debug = $("<ol id='osmio-modal-debug'></ol>").dblclick(function(){ $(this).toggleClass('osmio-modal-debug-collapsed'); });

	$(function(){
		
		modalWrapper.appendTo("body");
		debug.prependTo(modalWrapper);

	});


	// --------------------------------------------------
	// Модальник

	var osmioModal = function(obj) {

		var self = $(obj);
		var t = this;

		this.id = modals.length;
		modals[this.id] = t;

		var options = $.extend({}, defaultOptions);
		this.options = options;


		// --------------------------------------------------
		// Декорирование объекта

		// Перемещаем объект в глобальный враппер (гарант того, что сам враппер будет корректно абсолютно позиционироваться от левого-верхнего угла), а не от родителя
		// Этот момент следует иметь ввиду при использовании плагина
		self.detach().appendTo(modalWrapper).wrap("<div class='osmio-modal-wrapper'><div class='osmio-modal-container'><div class='osmio-modal-content'></div></div></div>").addClass('osmio-modal');

		var content = self.parent();
		var container = content.parent();
		var wrapper = container.parent();

		// Добавляем заголовок
		container.prepend("<a href='#' class='osmio-modal-close'>&times;</a><h1 class='osmio-modal-header'><em></em><span></span></h1>");

		var close		= container.find("a.osmio-modal-close");
		var header	= container.find("h1.osmio-modal-header");
		var caption	= container.find("h1.osmio-modal-header > span");

		// Элементы для передачи в коллбэки
		t.elements = {
			self:						self,
			wrapper:				wrapper,
			container:			container,
			content:				content,
			header:					header,
			caption:				caption,
			close:					close
		};


		// --------------------------------------------------
		// Действия

		/**
		 * Вывод сервисной информации
		 * @param html
		 */
		t.debug = function(html) {

			debug.append($("<li></li>").html(html)).scrollTop(100000); //.delay(5000).slideUp()

		};

		/**
		 * Проверка размера на попадание в заданные рамки
		 * @param width
		 * @param height
		 * @param availWidth
		 * @param availHeight
		 */
		t.checkSize = function(width, height, availWidth, availHeight) {

			if (width > options.maxWidth && options.maxWidth)	width = options.maxWidth;
			if (width > availWidth)														width = availWidth;
			if (width < options.minWidth)											width = options.minWidth;

			if (height > options.maxHeight && options.maxHeight)	height = options.maxHeight;
			if (height > availHeight)															height = availHeight;
			if (height < options.minHeight)												height = options.minHeight;

			return {width: width, height: height};

		};

		/**
		 * Вызвать обработчик
		 * @param event
		 */
		function callback(event) {

			self.trigger(event + ".osmio-modal", [t]);

		}

		/**
		 * Обработка действий
		 * @param action
		 * @param name
		 * @param value
		 */
		t.action = function(action, name, value) {

			if (!action) { action = {}; }

			if (typeof action == "object") {

				t.action("options", action);
				return;

			}

			if (typeof action == "string") {

				/**
				 * Открыть
				 */
				if (action == "open") {

					t.debug("Открытие лайтбокса");

					if (wrapper.is(":hidden")) {
						opened++;
						t.debug("Открыто лайтбоксов: " + opened);
						modalWrapper.show();
						wrapper.stop().show();
					}

					// Показываем лайтбокс
					zIndex++;
					wrapper.css({zIndex: zIndex});

					callback("open");

				/**
				 * Закрыть
				 */
				} else if (action == "close") {

					t.debug("Закрытие лайтбокса");

					if (wrapper.is(":visible")) {
						opened--;
						t.debug("Открыто лайтбоксов: " + opened);
					}

					zIndex--;
					wrapper.hide();
					if (opened == 0) modalWrapper.hide();

					callback("close");

				/**
				 * Обновить размер
				 */
				} else if (action == "resize") {

					if (wrapper.is(":hidden")) return;

					t.debug("Обновление размера");

					container.width("auto"); // IE Patch

					var dimensions = {};

					dimensions.scrollTop		= content.attr("scrollTop");
					dimensions.scrollLeft		= content.attr("scrollLeft");
					dimensions.headerHeight	= header.is(":visible") ? header.outerHeight(true) : 0;
					dimensions.modalWidth		= wrapper.width();
					dimensions.modalHeight	= wrapper.height();

					dimensions.availWidth		= dimensions.modalWidth		- (container.outerWidth()		- container.width())		- options.padding[1]	- options.padding[3] * 2;
					dimensions.availHeight	= dimensions.modalHeight	- (container.outerHeight()	- container.height())	- options.padding[0]		- options.padding[2] - dimensions.headerHeight;

					t.debug(
						"… Область: " + dimensions.availWidth + "&times;" + dimensions.availHeight + ", " +
						"Ширина: " + options.minWidth + "…" + options.maxWidth + ", Высота: " + options.minHeight + "…" + options.maxHeight + " " +
						"Отступы: " + options.padding[0] + " " + options.padding[1] + " " + options.padding[2] + " " + options.padding[3]
					);


					// Изменение размера

					// Восстанавливаем оригинальную ширину и высоту
					if ($.browser.msie && $.browser.version < 7) { // IE Patch

						content.width("").height("");

					} else {

						content.width("auto").height("auto");

					}

					var size = {
						width:	content.width(),
						height:	content.height()
					};

					if ("resize" in options.overrides && typeof options.overrides.resize == "function") {

						size = options.overrides.resize(t, dimensions);
						if (!size.width || !size.height) throw "Коллбэк ресайза вернул кривой параметр";

					}

					size = t.checkSize(size.width, size.height, dimensions.availWidth, dimensions.availHeight);

					// Центрально позиционируем

					container.width(size.width); // IE Patch
					content.width(size.width).height(size.height);

					var w = container.outerWidth();
					var h = container.outerHeight();

					container.css({
						left:	(dimensions.modalWidth	- w - (options.padding[3] - options.padding[1])) / 2,
						top:	(dimensions.modalHeight - h - (options.padding[2] - options.padding[0])) / 2
					});

					content.attr("scrollTop",		dimensions.scrollTop);
					content.attr("scrollLeft",	dimensions.scrollLeft);

					callback("resize");

				/**
				 * Выполнение произвольного кода в окружении
				 */
				} else if (action == "exec") {

					// В данном случае name  выступает как функция
					if (typeof name != "function") throw "Secong argument should be function, " + name + " given";

					name(t);

					return false;

				/**
				 * Добавить обработчик
				 */
				} else if (action == "bind") {

					self.bind(name + ".osmio-modal", value);

				/**
				 * Убрать обработчик
				 */
				} else if (action == "unbind") {

					self.unbind(name + ".osmio-modal", value);

				/**
				 * Установка нескольких опций
				 */
				} else if (action == "options") {

					if (typeof name != "object") {
						throw "Secong argument should be an object, " + name + " given";
					}

					for (i in name) {

						t.action("option", i, name[i]);

					}

				/**
				 * Установка опции
				 */
				} else if (action == "option") {

					if (!(name in defaultOptions)) throw "Unknown option: " + name;

					if (typeof options[name] != "object") {

						options[name] = value;

					} else {

						if (typeof value != "object") throw "Value should be an object, " + value + " given for " + name;

						options[name] = $.extend({}, options[name], value);

					}

					if (name == "title") {

						if (value) {
							caption.html(value);
							header.show();
						} else header.hide();

					}

					if (name == "padding") {

						if (typeof options.padding != "object") options.padding = [options.padding, options.padding, options.padding, options.padding];

					}

					t.action("resize");

				/**
				 * Установка опции
				 */
				} else if (action == "debug") {

					if (name) debug.show(); else debug.hide();

				} else {

					throw "Unknown action: " + action;

				}

			} else {

				throw "Unknown action format";

			}

		};


		// --------------------------------------------------
		// Обработка действий юзера

		// Закрытие по клику по «x»
		close.click(function(){ t.action("close"); return false; });

		// Закрытие по ESC
		$(document).keyup(function(e){ if (e.which == 27) t.action("close"); });

		// Закрытие по клику вне враппера
		wrapper.click(function(e){ if (e.target == this) t.action("close"); }); // TODO IE не дает ловить клики по слою с фильтром (градиент)

		// Отработка изменения размера
		$(window).resize(function(){ t.action("resize"); });

		// Каждые пол-секунды проводим ресайз, на тот случай, если текст внутри изменился
		var contentTempHeight = 0;
		setInterval(function(){

			if (wrapper.is(":visible") && contentTempHeight != content.attr("scrollHeight")) {

				contentTempHeight = content.attr("scrollHeight");

				t.action("resize");

			}

		}, 100);

	};


	// --------------------------------------------------
	// Создание лайтбоксов из элементов

	/**
	 * Основная внешняя функция плагина
	 * @param action
	 * @param name
	 * @param value
	 */
	$.fn.osmioModal = function(action, name, value) {

		$(this).each(function(){

			var obj = {};
			var id	= $(this).data("osmio-modal");

			if (!id) {

				// Создаем объект новый
				obj = new osmioModal(this);
				$(this).data("osmio-modal", obj.id);

			} else {

				// Вытаскиваем объект из массива
				obj = modals[id];

			}

			// Отправялем действие в модальник
			obj.action(action, name, value);

		});

		return this;

	}; // end of $.fn.osmioModal

})(jQuery);